import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;

public class Map {
	
	private int rows = 13;
	private int columns = 17;
	
	private Block[][] blocks = new Block[rows][columns];
	
	private User user;
	private ArrayList<Monster> monsters;
	private ArrayList<Block> explodingBlocks = new ArrayList<Block>();
	
	private Timer timer = SimulationRunner.getTimer();
	private int m_life_left = 3;
	private int m_points;
	private int m_time = 180;
	
	private DoorFeatureBlock dfb = null;
	
	public void initializeMap() {
		timer.scheduleAtFixedRate(new MyTimerTask(){
        	public void run(){
        		--m_time;
        		if(m_time == 0){
        			m_time = 180;
        			timer.purge();
        			timer.cancel();
        			SimulationRunner.refreshTimer();
        			timer = SimulationRunner.getTimer();
        			initializeMap();
        		}
        	}
        },0, 1000 );
		
		if (user != null){
			user.removeBomb();
			-- m_life_left;
			if(m_life_left == -1){
				System.out.println("GAME OVER");
				System.exit(0);
			}
		}
		
		ArrayList<DynamicBlock> dynamicBlocks = new ArrayList<DynamicBlock>();
		
		Random random = new Random();
		
		// Fill the map with EmptyBlocks
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				if (i == 0 || j == 0 || i == rows-1 || j == columns-1 || (i%2 == 0 && j%2 == 0)) {
					blocks[i][j] = new StaticBlock();
				} else {
					float randomNumber = random.nextFloat();
					if (randomNumber < 0.25) {
						if ((i == 1 && j == 1)
								|| (i == 1 && j == 2)
								|| (i == 2 && j == 1)) {
							blocks[i][j] = new EmptyBlock();
						} else {
							DynamicBlock dBlock = new DynamicBlock();
							blocks[i][j] = dBlock;
							dynamicBlocks.add(dBlock);
						}
					}
					else {
						blocks[i][j] = new EmptyBlock();
					}
				}
				
				if (dfb != null)
					System.out.println(j + " " + i);
				
				blocks[i][j].setX(j*Entity.getEntityWidth());
				blocks[i][j].setY(i*Entity.getEntityHeight());
			}
		}
		// Initialize user's coordinates and step sizes
		if (user == null){
			user = new User(this);
		}
		user.setX(Entity.getEntityWidth());
		user.setY(Entity.getEntityHeight());
		user.setStepX(5);
		user.setStepY(5);
		
		// Initialize monsters
		monsters = new ArrayList<Monster>();
		
		for (int i = 0; i < 5; i++) {
			Monster monster = new Monster(this);
			monster.setStrategy(new NormalStrategy(this,monster,user));
			
			// Look for a random empty space to place the newly created monster
			while (true) {
				int indexX = random.nextInt(rows);
				int indexY = random.nextInt(columns);
				
				if ((indexX == 1 && indexY == 1)
						|| (indexX == 1 && indexY == 2)
						|| (indexX == 2 && indexY == 1)) {
					continue;
				}
				
				Block block = blocks[indexX][indexY];
				
				// If block is not available, continue immediately with another random block
				if (!block.isMoveable()) {
					continue;
				}
				
				// Check if there is already a monster on the selected block
				boolean isThereAMonster = true;
				
				for (Monster m : monsters) {
					if (m.getX() == block.getX() && m.getY() == block.getY()) {
						isThereAMonster = false;
						break;
					}
				}
				
				if (!isThereAMonster) {
					continue;
				}
				
				monster.setX(block.getX());
				monster.setY(block.getY());
				monster.setStepX(2);
				monster.setStepY(2);
				
				monsters.add(monster);
				
				break;
			}
		}
		
		// Place DoorFeatureBlock
		int asd = random.nextInt(dynamicBlocks.size());
		dynamicBlocks.get(asd).setFeatureBlock(new DoorFeatureBlock());
		
		explodingBlocks.clear(); 
	}
	
	public Map() {
		initializeMap(); 
	}
	
	public boolean isIndexMoveable(int x, int y){
		return blocks[y][x].isMoveable();
	}
	
	public void drawAll(Graphics2D g2d) {
		// Draw the blocks
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				blocks[i][j].draw(g2d);
			}
		}
		
		// Draw the user
		user.draw(g2d);
		
		// Draw the monsters
		for (Monster monster : monsters) {
			monster.draw(g2d);
		}
	}
	
	public User getUser(){
		return user;
	}
	
	public void stepAll() {
		// Step the user
		user.step();
		
		// Step the monsters
		for (Monster monster : monsters) {
			monster.step();
		}
	}
	
	public int getRows()
	{
		return rows;
	}
	
	public int getColumns()
	{
		return columns;
	}
	
	public void handleBombExplosion(int x, int y,int range, boolean changeColour) {
		
		// Clear the exploding blocks list if explosion is over
		if (!changeColour) {
			explodingBlocks.clear();
		}
		
		int indexX = x/Entity.getEntityWidth();
		int indexY = y/Entity.getEntityHeight();
		Block block = blocks[indexY][indexX];
		
		if(block.isRemovable()){
			if (block.isMoveable()){
				EmptyBlock emptyBlock = (EmptyBlock)block;
				emptyBlock.changeColour(changeColour);
			}
			
			if (changeColour) {
				explodingBlocks.add(block);
			}
		}
		
		for (int i=1; i<=range; i++){
			block = blocks[indexY][indexX+i];
			convertToDynamicBlock(changeColour, indexX+i, indexY, block);
		}
		for (int i=1; i<=range; i++){
			block = blocks[indexY][indexX-i];
			convertToDynamicBlock(changeColour, indexX-i, indexY, block);
		}
		for (int i=1; i<=range; i++){
			block = blocks[indexY-i][indexX];
			convertToDynamicBlock(changeColour, indexX, indexY-i, block);
		}
		for (int i=1; i<=range; i++){
			block = blocks[indexY+i][indexX];
			convertToDynamicBlock(changeColour, indexX, indexY+i, block);
		}
		
	}

	private void convertToDynamicBlock(boolean changeColour, int indexX,
			int indexY, Block block) {
		DynamicBlock db;
		EmptyBlock emptyBlock;
		if(block.isRemovable()){
			if (!block.isMoveable()){
				db = (DynamicBlock)block;
				m_points += db.getPoint();
				
				if (db.hasFeatureBlock()) {
					blocks[indexY][indexX] = db.getFeatureBlock();
					blocks[indexY][indexX].setX(db.getX());
					blocks[indexY][indexX].setY(db.getY());
				} else {
					emptyBlock = new EmptyBlock();
					emptyBlock.setX(block.getX());
					emptyBlock.setY(block.getY());
					blocks[indexY][indexX] = emptyBlock;
					emptyBlock.changeColour(changeColour);
				}
			} else {
				emptyBlock = (EmptyBlock)block;
				emptyBlock.changeColour(changeColour);
			}
			
			if (changeColour) {
				explodingBlocks.add(block);
			}
		}
	}
	
	public ArrayList<Monster> getMonsters() {
		return monsters;
	}
	
	public ArrayList<Block> getExplodingBlocks() {
		return explodingBlocks;
	}

	public void handleUserMonsterCollision() {
		killUser();
		System.out.println("Ooops User touched a monster!");
	}

	public void handleUserBombCollision() {
		killUser();
		System.out.println("Ooops User touched a bomb!");
	}
	
	private void killUser() {
		timer.cancel();
		timer.purge();
		SimulationRunner.refreshTimer();
		timer = SimulationRunner.getTimer();
		m_time = 180;
		initializeMap();
	}

	public void handleMonsterBombCollision(ArrayList<Monster> deadMonsters) {
		for (Monster deadMonster : deadMonsters) {
			m_points += deadMonster.getPoint(); 
			this.monsters.remove(deadMonster);
		}
	}
	
	public void setMoveableBlock(int x, int y, boolean moveable){
		blocks[y][x].setMoveable(moveable);
	}

	public int getLifeLeft() {
		return m_life_left;
	}

	public int getPoints() {
		return m_points;
	}

	public int getTimeLeft() {
		return m_time;
	}
}